package jp.co.nes.awssupport.common.utility;

import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import jp.co.nes.awssupport.common.base.CommonException;
import jp.co.nes.awssupport.common.bean.BaseBean;

/**
 * Jsonユーティリティです。
 */
public class JsonUtil {

    /** ObjectMapper */
    private static ObjectMapper mapper;

    /**
     * 初期化処理です。
     */
    static {
        // ObjectMapperはスレッドセーフ
        mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.PUBLIC_ONLY);
    }

    /**
     * デフォルトコンストラクタです。<br>
     * 本クラスは生成しません。
     */
    private JsonUtil() {
    }

    /**
     * BeanクラスをJson文字列に変換し、返却します。
     *
     * @param bean 変換するBeanクラスのインスタンス
     * @return 変換結果
     */
    public static <T extends BaseBean> String convertBeanToJsonString(T bean) {
        try {
            return mapper.writeValueAsString(bean);
        } catch (JsonProcessingException e) {
            throw new CommonException(e);
        }
    }

    /**
     * Json文字列をBeanクラスに変換し、返却します。
     *
     * @param json 変換するJson文字列
     * @param targetClass 変換するBeanクラスのClassオブジェクト
     * @return 変換結果
     */
    public static <T extends BaseBean> T convertJsonStringToBean(String json, Class<T> targetClass) {
        try {
            return mapper.readValue(json, targetClass);
        } catch (IOException e) {
            throw new CommonException(e);
        }
    }

    /**
     * 入力ストリーム(Json)からBeanクラスに変換し、返却します。
     *
     * @param json 入力ストリーム(Json)
     * @param targetClass 変換するBeanクラスのClassオブジェクト
     * @return 変換結果
     */
    public static <T extends BaseBean> T convertJsonInputStreamToBean(InputStream json, Class<T> targetClass) {
        try {
            return mapper.readValue(json, targetClass);
        } catch (IOException e) {
            throw new CommonException(e);
        }
    }

    /**
     * Json文字列をマップに変換し、返却します。
     *
     * @param json 変換するJson文字列
     * @return 変換結果
     */
    public static Map<String, String> convertJsonStringToMap(String json) {
        try {
            return mapper.readValue(json, new TypeReference<Map<String, String>>() {
            });
        } catch (IOException e) {
            throw new CommonException(e);
        }
    }

    /**
     * BeanクラスをJson文字列に変換し、writerで出力します。
     *
     * @param writer Writer
     * @param bean 出力するBeanクラス
     */
    public static <T extends BaseBean> void writeValue(Writer writer, T bean) {
        try {
            mapper.writeValue(writer, bean);
        } catch (IOException e) {
            throw new CommonException(e);
        }
    }
}
